package com.dycx.api.controller.app;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.dycx.common.HttpServletUtils;
import com.dycx.common.RestResponse;
import com.dycx.common.ValidationGroup;
import com.dycx.common.XMLUtil;
import com.dycx.common.annotation.Authorization;
import com.dycx.common.annotation.WriterLog;
import com.dycx.common.context.UserInfoContext;
import com.dycx.payment.enums.PayBizTypeEnum;
import com.dycx.common.enums.SourceEnum;
import com.dycx.common.vendor.weixin.JSSDKUtils;
import com.dycx.common.vendor.weixin.vo.JsConfigVO;
import com.dycx.payment.dto.PaymentOrderDTO;
import com.dycx.payment.entity.PaymentOrderDO;
import com.dycx.payment.service.PayService;
import com.dycx.payment.service.PaymentOrderService;
import com.dycx.system.service.ConfigService;
import com.dycx.system.vo.SystemConfig;
import com.dycx.user.entity.FansDO;
import com.dycx.user.entity.UserDO;
import com.dycx.user.service.FansService;
import com.dycx.user.service.UserService;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.model.Transaction;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;


@RestController
@RequestMapping(value = "/app/wx")
@Slf4j
@AllArgsConstructor
public class WxController {

    private ConfigService configService;
    private FansService fansService;
    private PaymentOrderService paymentOrderService;
    private PayService payService;
    private UserService userService;

    @GetMapping("getJsConfig")
    public RestResponse<JsConfigVO> getJsConfig(String url) {
        SystemConfig systemConfig = configService.getSystemConfig();
        try {
            return RestResponse.success(JSSDKUtils.sign(url, systemConfig.getWxMpAppId(), systemConfig.getWxMpAppSecret()));
        } catch (Exception ex) {
            throw new RuntimeException(ex.getMessage());
        }
    }

    /**
     * 公众号消息推送
     *
     * @param xmldata
     */
    @PostMapping("/acceptWxPushMsg")
    public void acceptWxPushMsg(@RequestBody String xmldata) {
        String msg = "<xml>\n" +
                "  <ToUserName><![CDATA[toUser]]></ToUserName>\n" +
                "  <FromUserName><![CDATA[FromUser]]></FromUserName>\n" +
                "  <CreateTime>123456789</CreateTime>\n" +
                "  <MsgType><![CDATA[event]]></MsgType>\n" +
                "  <Event><![CDATA[subscribe]]></Event>\n" +
                "  <EventKey><![CDATA[qrscene_123123]]></EventKey>\n" +
                "  <Ticket><![CDATA[TICKET]]></Ticket>\n" +
                "</xml>";

        Map<String, String> map = XMLUtil.xmlStringToMap(msg);
        if (map.get("Event").equals("subscribe")) {
            FansDO fansDO = new FansDO();
            fansDO.setOpenid(map.get("FromUserName"));
            fansDO.setSceneId(Convert.toInt(map.get("EventKey").replace("qrscene_", "")));
            fansDO.setFollowTime(LocalDateTimeUtil.ofUTC(Convert.toLong(map.get("CreateTime"))));
            fansDO.setFollowStatus(true);
            fansDO.setCredit(30);
            fansService.save(fansDO);
        }
    }

    @Authorization
    @PostMapping("/wxPay")
    public RestResponse<?> wxPay(@RequestBody  @Validated(ValidationGroup.Pay.class) PaymentOrderDTO dto,
                                 @RequestHeader("source") Integer source) {
        dto.setUid(UserInfoContext.get().getUserId());
        dto.setSource(source);

        if (SourceEnum.WX_MP.getCode().equals(source)) {
            UserDO userDO = userService.getById(dto.getUid());
            Assert.notNull(userDO, "用户信息不正确");
            dto.setOpenId(userDO.getWxMpOpenId());
        } else if (SourceEnum.WX_MINI_APP.getCode().equals(source)) {
            UserDO userDO = userService.getById(dto.getUid());
            Assert.notNull(userDO, "用户信息不正确");
            dto.setOpenId(userDO.getWxMiniOpenId());
        }

        return payService.pay(dto);
    }

    /***
     * 微信支付回调通知
     * @param request
     * @return
     * @throws
     */
    @WriterLog
    @PostMapping("/wxPayNotify")
    public RestResponse<T> wxPayNotify(HttpServletRequest request) throws Exception {
        log.info("进入回调：========================================");

        // 请求头Wechatpay-Signature
        String signature = request.getHeader("Wechatpay-Signature");
        // 请求头Wechatpay-nonce
        String nonce = request.getHeader("Wechatpay-Nonce");
        // 请求头Wechatpay-Timestamp
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        // 微信支付证书序列号
        String serial = request.getHeader("Wechatpay-Serial");
        // 签名方式
        String signType = request.getHeader("Wechatpay-Signature-Type");

        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(serial)
                .nonce(nonce)
                .signature(signature)
                .timestamp(timestamp)
                .signType(signType)
                .body(HttpServletUtils.getRequestBody(request))
                .build();

        SystemConfig sysConfig = configService.getSystemConfig();

        NotificationConfig config = new RSAAutoCertificateConfig.Builder()
                .merchantId(sysConfig.getWxPayMerchantId())
                .privateKeyFromPath(sysConfig.getWxPayPrivateKeyPath())
                .merchantSerialNumber(sysConfig.getWxPayMerchantSerialNumber())
                .apiV3Key(sysConfig.getWxPayApiV3Key())
                .build();

        NotificationParser parser = new NotificationParser(config);

        // 以支付通知回调为例，验签、解密并转换成 Transaction

        Transaction transaction = parser.parse(requestParam, Transaction.class);


        //*****************************************处理支付单数据********************************************
        LambdaQueryWrapper<PaymentOrderDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PaymentOrderDO::getOutTradeNo, transaction.getOutTradeNo());
        PaymentOrderDO paymentOrderDO = paymentOrderService.getOne(wrapper);
        if (!paymentOrderDO.getAmount().equals(transaction.getAmount().getPayerTotal()) || paymentOrderDO.getResult()) {
            return RestResponse.fail();
        }

        paymentOrderDO.setTransactionId(transaction.getTransactionId());
        paymentOrderDO.setPayTime(LocalDateTimeUtil.parse(transaction.getSuccessTime().replace("+08:00", "")));
        paymentOrderDO.setResult(true);
        paymentOrderService.updateById(paymentOrderDO);


        //*****************************************处理业务数据********************************************
        if(PayBizTypeEnum.PAY_VIP.getCode().equals(paymentOrderDO.getPayBizType())){
            return userService.payNotify(paymentOrderDO);
        }

        return RestResponse.fail();
    }

    /**
     * 查询支付状态
     **/
    @Authorization
    @PostMapping("queryPayStatus")
    public RestResponse<?> queryPayStatus(String outTradeNo) {
        LambdaQueryWrapper<PaymentOrderDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PaymentOrderDO::getOutTradeNo, outTradeNo);
        PaymentOrderDO paymentOrderDO = paymentOrderService.getOne(wrapper);
        Assert.notNull(paymentOrderDO, "支付订单创建失败");
        if (!paymentOrderDO.getResult()) RestResponse.fail("支付失败");

        return RestResponse.success();
    }
}

